<template>
  <div v-loading="loading">
    <div class="category-search">
      <el-form :inline="true" :model="query" ref="formRef" class="demo-form-inline">
        <el-form-item label="分类名称" prop="name">
          <el-input v-model="query.name" placeholder="分类名称" />
        </el-form-item>
        <el-form-item label="递归查询" prop="recursive">
          <el-switch v-model="query.recursive" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="loadData">查询</el-button>
          <el-button @click="resetForm(formRef)">重置</el-button>
        </el-form-item>
      </el-form>
    </div>

    <div class="category-tool-bar">
      <div class="zw"></div>
      <div>
        <el-form label-width="120px" :inline="true">
          <el-form-item label="级联删除">
            <el-switch v-model="cascade" />
          </el-form-item>
          <el-form-item>
            <el-button type="default" @click="onAdd({ id: '0', name: '根节点', sortNum: 0 })">
              添加
              <el-icon class="el-icon--right"><Plus /></el-icon>
            </el-button>
            <el-button type="danger" @click="onBatchDelete()" :disabled="deleteDisabled">
              删除
              <el-icon class="el-icon--right"><Delete /></el-icon>
            </el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>

    <div class="category-content">
      <el-tree
        ref="treeEl"
        node-key="id"
        :data="treeData"
        :props="defaultProps"
        :load="loadNode"
        :default-expand-all="defaultExpandAll"
        :draggable="editable"
        @check="handleNodeEvent"
        lazy
        show-checkbox
      >
        <template #default="{ data }">
          <div>
            <span v-if="data.icon">
              <el-icon>
                <component :is="data.icon"></component>
              </el-icon>
            </span>
            <span>
              {{ data.name }}
            </span>
            <span v-if="editable" class="category-edit" @click.stop>
              <el-link title="添加" @click="onAdd(data)">
                <el-icon><Plus /></el-icon
              ></el-link>
              <el-link title="编辑" @click="onUpdate(data)"
                ><el-icon><Edit /></el-icon
              ></el-link>
              <el-link title="删除" @click="onDelete(data)"
                ><el-icon><Delete /></el-icon
              ></el-link>
            </span>
          </div>
        </template>
      </el-tree>
    </div>
  </div>

  <el-dialog v-model="dialogVisible" :title="form.id === '' ? '添加' : '编辑'" width="30%">
    <el-form :model="form" :rules="rules" ref="ruleFormRef" label-position="left" label-width="80">
      <el-form-item label="上级分类" prop="parentId">
        <el-tree-select
          v-model="form.parentId"
          node-key="id"
          :props="defaultProps"
          :load="loadNode"
          :render-after-expand="false"
          :cache-data="cacheData"
          lazy
          check-strictly
          check-on-click-node
          :disabled="parentDisabled"
          style="width: 100%"
        />
      </el-form-item>
      <el-form-item label="分类名称" prop="name" required>
        <el-input v-model="form.name" />
      </el-form-item>
      <el-form-item label="排序号" prop="sortNum">
        <el-input-number v-model="form.sortNum" style="width: 100%" />
      </el-form-item>
    </el-form>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm(ruleFormRef)" :disabled="submitDisabled">
          确定
        </el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import { reactive, ref, toRaw } from 'vue'
import { ElMessage, ElMessageBox, ElTree, FormInstance, FormRules } from 'element-plus'
import { addApi, deleteApi, getByIdApi, loadListApi, updateApi } from '@/api/category'
import { CategoryModel, CategoryQuery } from '@/api/model'
import Node from 'element-plus/es/components/tree/src/model/node'
import { TreeData, TreeOptionProps } from 'element-plus/es/components/tree/src/tree.type'

const formRef = ref<FormInstance>()

const treeEl = ref<InstanceType<typeof ElTree>>()
const loading = ref(false)
const treeData = ref<CategoryModel[]>([])
const query = ref<CategoryQuery>({})
const defaultExpandAll = ref(false)
const editable = ref(true)
const dialogVisible = ref(false)
const parentDisabled = ref(false)
const submitDisabled = ref(false)
const deleteDisabled = ref(true)
const cascade = ref(false)
const ruleFormRef = ref<FormInstance>()

let form = ref<CategoryModel>({
  id: '',
  parentId: '0',
  name: '',
  sortNum: 0
})

const rules = reactive<FormRules>({
  parentId: [{ required: true, message: '上级分类不能为空', trigger: 'blur' }],
  name: [
    { required: true, message: '请输入分类名称', trigger: 'blur' },
    { min: 1, max: 20, message: '分类名称长度最长20个字符', trigger: 'blur' }
  ],
  sortNum: [{ required: true, message: '请输入排序号', trigger: 'blur' }]
})

let cacheData: CategoryModel[] = [{ id: '0', name: '根节点', sortNum: 0 }]

const defaultProps: TreeOptionProps = {
  label: 'name',
  children: 'children',
  isLeaf: 'isLeaf'
}

const loadNode = async (node: Node, resolve: (nodes: TreeData[]) => void) => {
  loading.value = true
  try {
    let parentId = '0'
    if (node.level !== 0) {
      parentId = node.data.id
    }
    const children = await loadChildren(parentId)
    node.data.children = children
    resolve(node.data.children)
  } catch {
    return
  } finally {
    loading.value = false
  }
}

const loadData = async () => {
  loading.value = true
  try {
    const param = toRaw(query.value)
    param.parentId = param.name ? undefined : '0'
    const list = await loadListApi(param)
    list.sort((a, b) => a.sortNum - b.sortNum)
    treeData.value = list
    deleteDisabled.value = true
  } catch {
    return
  } finally {
    loading.value = false
  }
}

const submitForm = async (formEl: FormInstance | undefined) => {
  submitDisabled.value = true
  try {
    if (!formEl) return
    await formEl.validate((valid) => {
      if (!valid) {
        return
      }
    })
    const param: CategoryModel = { ...form.value }
    if (form.value.id === '') {
      await addApi(param)
    } else {
      await updateApi(param)
    }
    ElMessage({
      message: '保存成功',
      type: 'success'
    })
    formEl.resetFields()
    dialogVisible.value = false
    loadData()
  } catch {
    return
  } finally {
    submitDisabled.value = false
  }
}

const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.resetFields()
}

const loadChildren = async (parentId: string) => {
  // 发送异步请求获取节点的子节点数据
  const children = await loadListApi({ parentId: parentId })
  children.sort((a, b) => a.sortNum - b.sortNum)
  // 将子节点数据转换成 Tree 组件需要的格式
  return children
}

const handleNodeEvent = () => {
  const keys = treeEl.value!.getCheckedKeys(false)
  deleteDisabled.value = keys.length === 0
}

const onBatchDelete = async () => {
  const keys = treeEl.value!.getCheckedKeys(false)
  if (keys.length === 0) {
    ElMessage({
      message: '请选择要删除的分类',
      type: 'warning'
    })
    return
  }

  ElMessageBox.confirm(`确认要删除吗?`)
    .then(async () => {
      await deleteApi(keys as string[], cascade.value)
      ElMessage({
        message: '删除成功',
        type: 'success'
      })
      loadData()
    })
    .catch(() => {})
}

const onAdd = (data: CategoryModel) => {
  parentDisabled.value = data.id === '0'
  cacheData = [data]
  form.value.parentId = data.id
  form.value.name = ''
  form.value.id = ''
  form.value.sortNum = data.sortNum
  dialogVisible.value = true
}

const onUpdate = async (data: CategoryModel) => {
  parentDisabled.value = data.parentId === '0'
  if (data.parentId === '0') {
    cacheData = [{ id: '0', name: '根节点', sortNum: 0 }]
  } else {
    cacheData = [await getByIdApi(data.parentId)]
  }
  form.value = { ...data }
  dialogVisible.value = true
}

const onDelete = (data: CategoryModel) => {
  ElMessageBox.confirm('确认要删除吗?')
    .then(async () => {
      await deleteApi([data.id], cascade.value)
      ElMessage({
        message: '删除成功',
        type: 'success'
      })
      loadData()
    })
    .catch(() => {})
}
</script>

<style lang="scss" scoped>
.category-edit {
  margin: 0 10px;
  position: absolute;
  right: 10px;
  .el-link + .el-link {
    margin-left: 4px;
  }
}
.category-tool-bar {
  width: 100%;
  display: flex;
  border-left: 1px solid var(--el-border-color);
  border-right: 1px solid var(--el-border-color);
  border-bottom: 1px solid var(--el-border-color);
  .zw {
    flex: 2;
  }
  .el-form-item {
    margin-bottom: 0px;
  }
}
.category-search {
  border: 1px solid var(--el-border-color);
  .demo-form-inline {
    margin: 10px;
  }
  .el-form-item {
    margin-bottom: 0px;
  }
}
</style>
